home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Games / NetHack 3.1.3 / source / src / pager.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-08-01  |  17.0 KB  |  672 lines  |  [TEXT/R*ch]

  1. /*    SCCS Id: @(#)pager.c    3.1    93/05/26    */
  2. /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
  3. /* NetHack may be freely redistributed.  See license for details. */
  4.  
  5. /* This file contains the command routines dowhatis() and dohelp() and */
  6. /* a few other help related facilities */
  7.  
  8. #include "hack.h"
  9.  
  10. #ifndef SEEK_SET
  11. #define SEEK_SET 0
  12. #endif
  13.  
  14. static boolean FDECL(is_swallow_sym, (int));
  15. static int FDECL(append_str, (char *, const char *));
  16. static void FDECL(lookat, (int, int, char *));
  17. static void FDECL(checkfile, (char *, BOOLEAN_P));
  18. static int FDECL(do_look, (BOOLEAN_P));
  19. static char NDECL(help_menu);
  20. #ifdef PORT_HELP
  21. extern void NDECL(port_help);
  22. #endif
  23.  
  24. /* Returns "true" for characters that could represent a monster's stomach. */
  25. static boolean
  26. is_swallow_sym(c)
  27. int c;
  28. {
  29.     int i;
  30.     for (i = S_sw_tl; i <= S_sw_br; i++)
  31.     if ((int)showsyms[i] == c) return TRUE;
  32.     return FALSE;
  33. }
  34.  
  35. /*
  36.  * Append new_str to the end of buf if new_str doesn't already exist as
  37.  * a substring of buf.  Return 1 if the string was appended, 0 otherwise.
  38.  * It is expected that buf is of size BUFSZ.
  39.  */
  40. static int
  41. append_str(buf, new_str)
  42.     char *buf;
  43.     const char *new_str;
  44. {
  45.     int space_left;    /* space remaining in buf */
  46.  
  47.     if (strstri(buf, new_str)) return 0;
  48.  
  49.     space_left = BUFSZ - strlen(buf) - 1;
  50.     (void) strncat(buf, " or ", space_left);
  51.     (void) strncat(buf, new_str, space_left - 4);
  52.     return 1;
  53. }
  54.  
  55. /*
  56.  * Return the name of the glyph found at (x,y).
  57.  */
  58. static void
  59. lookat(x, y, buf)
  60.     int x, y;
  61.     char *buf;
  62. {
  63.     register struct monst *mtmp;
  64.     struct trap *trap;
  65.     register char *s, *t;
  66.     int glyph;
  67.  
  68.     buf[0] = 0;
  69.     glyph = glyph_at(x,y);
  70.     if (u.ux == x && u.uy == y && canseeself()) {
  71.     Sprintf(buf, "%s%s called %s",
  72.         Invis ? "invisible " : "",
  73. #ifdef POLYSELF
  74.         u.mtimedone ? mons[u.umonnum].mname :
  75. #endif
  76.         player_mon()->mname, plname);
  77.     }
  78.     else if (u.uswallow) {
  79.     /* all locations when swallowed other than the hero are the monster */
  80.     Sprintf(buf, "interior of %s",
  81.                     Blind ? "a monster" : a_monnam(u.ustuck));
  82.     }
  83.     else if (glyph_is_monster(glyph)) {
  84.     bhitpos.x = x;
  85.     bhitpos.y = y;
  86.     mtmp = m_at(x,y);
  87.     if(mtmp != (struct monst *) 0) {
  88.         register boolean hp = (mtmp->data == &mons[PM_HIGH_PRIEST]);
  89.  
  90.         Sprintf(buf, "%s%s%s",
  91.             (!hp && mtmp->mtame && !Hallucination) ? "tame " :
  92.             (!hp && mtmp->mpeaceful && !Hallucination) ?
  93.                                                   "peaceful " : "",
  94.             (hp ? "high priest" : l_monnam(mtmp)),
  95.             u.ustuck == mtmp ?
  96. #ifdef POLYSELF
  97.             ((u.mtimedone && sticks(uasmon)) ? ", being held" :
  98. #endif
  99.              ", holding you"
  100. #ifdef POLYSELF
  101.              )
  102. #endif
  103.             : "");
  104.     }
  105.     }
  106.     else if (glyph_is_object(glyph)) {
  107.     struct obj *otmp = vobj_at(x,y);
  108.  
  109.     if(otmp == (struct obj *) 0 || otmp->otyp != glyph_to_obj(glyph)) {
  110.         if(glyph_to_obj(glyph) != STRANGE_OBJECT) {
  111.         otmp = mksobj(glyph_to_obj(glyph), FALSE, FALSE);
  112.         if(otmp->oclass == GOLD_CLASS)
  113.             otmp->quan = 2L; /* to force pluralization */
  114.         Strcpy(buf, distant_name(otmp, xname));
  115.         dealloc_obj(otmp);
  116.         }
  117.     } else
  118.         Strcpy(buf, distant_name(otmp, xname));
  119.  
  120.     if (levl[x][y].typ == STONE || levl[x][y].typ == SCORR)
  121.         Strcat(buf, " embedded in stone");
  122.     else if (IS_WALL(levl[x][y].typ) || levl[x][y].typ == SDOOR)
  123.         Strcat(buf, " embedded in a wall");
  124.     else if (closed_door(x,y))
  125.         Strcat(buf, " embedded in a door");
  126.     else if (is_pool(x,y))
  127.         Strcat(buf, " in water");
  128.     else if (is_lava(x,y))
  129.         Strcat(buf, " in molten lava");    /* [can this ever happen?] */
  130.     }
  131.     else if (glyph_is_trap(glyph)) {
  132.     if ((trap = t_at(x, y)) != 0) {
  133.         if (trap->ttyp == WEB)
  134.         Strcpy(buf, "web");
  135.         else {
  136.         Strcpy(buf, traps[ Hallucination ?
  137.                      rn2(TRAPNUM-3)+3 : trap->ttyp]);
  138.         /* strip leading garbage */
  139.         for (s = buf; *s && *s != ' '; s++) ;
  140.         if (*s) ++s;
  141.         for (t = buf; (*t++ = *s++) != 0; ) ;
  142.         }
  143.     }
  144.     }
  145.     else if(!glyph_is_cmap(glyph))
  146.     Strcpy(buf,"dark part of a room");
  147.     else switch(glyph_to_cmap(glyph)) {
  148.     case S_altar:
  149.         if(!In_endgame(&u.uz))
  150.         Sprintf(buf, "%s altar",
  151.         align_str(Amask2align(levl[x][y].altarmask & ~AM_SHRINE)));
  152.     else Sprintf(buf, "aligned altar");
  153.     break;
  154.     case S_ndoor:
  155.     if((levl[x][y].doormask & ~D_TRAPPED) == D_BROKEN)
  156.         Strcpy(buf,"broken door");
  157.     else
  158.         Strcpy(buf,"doorway");
  159.     break;
  160.     default:
  161.     Strcpy(buf,defsyms[glyph_to_cmap(glyph)].explanation);
  162.     break;
  163.     }
  164. }
  165.  
  166. /*
  167.  * Look in the "data" file for more info.  Called if the user typed in the
  168.  * whole name (user_typed_name == TRUE), or we've found a possible match
  169.  * with a character/glyph and flags.help is TRUE.
  170.  *
  171.  * NOTE: when (user_typed_name == FALSE), inp is considered read-only and 
  172.  *     must not be changed directly, e.g. via lcase(). Permitted are
  173.  *     functions, e.g. makesingular(), which operate on a copy of inp.
  174.  */
  175. static void
  176. checkfile(inp, user_typed_name)
  177.     char *inp;
  178.     boolean user_typed_name;
  179. {
  180.     FILE *fp;
  181.     char buf[BUFSZ];
  182.     char *ep;
  183.     long txt_offset;
  184.     boolean found_in_file = FALSE;
  185.  
  186.     fp = fopen_datafile(DATAFILE, "r");
  187.     if (!fp) {
  188.     pline("Cannot open data file!");
  189.     return;
  190.     }
  191.  
  192.     if (!strncmp(inp, "interior of ", 12))
  193.     inp += 12;
  194.     if (!strncmp(inp, "a ", 2))
  195.     inp += 2;
  196.     else if (!strncmp(inp, "an ", 3))
  197.     inp += 3;
  198.     else if (!strncmp(inp, "the ", 4))
  199.     inp += 4;
  200.     if (!strncmp(inp, "tame ", 5))
  201.     inp += 5;
  202.     else if (!strncmp(inp, "peaceful ", 9))
  203.     inp += 9;
  204.     if (!strncmp(inp, "invisible ", 10))
  205.     inp += 10;
  206.  
  207.     /* Make sure the name is non-empty. */
  208.     if (*inp) {
  209.     /* adjust the input to remove "named " and convert to lower case */
  210.     char *alt = 0;    /* alternate description */
  211.     if ((ep = strstri(inp, " named ")) != 0)
  212.         alt = ep + 7;
  213.     else
  214.         ep = strstri(inp, " called ");
  215.     if (ep) *ep = '\0';
  216.     if (user_typed_name)
  217.         (void) lcase(inp);
  218.  
  219.     /*
  220.      * If the object is named, then the name is the alternate description;
  221.      * otherwise, the result of makesingular() applied to the name is. This
  222.      * isn't strictly optimal, but named objects of interest to the user
  223.      * will usually be found under their name, rather than under their
  224.      * object type, so looking for a singular form is pointless.
  225.      */
  226.  
  227.     if (!alt)
  228.         alt = makesingular(inp);
  229.     else
  230.         if (user_typed_name)
  231.             (void) lcase(alt);
  232.  
  233.     /* skip first record; read second */
  234.     txt_offset = 0L;
  235.     if (!fgets(buf, BUFSZ, fp) || !fgets(buf, BUFSZ, fp)) {
  236.         impossible("can't read 'data' file");
  237.         (void) fclose(fp);
  238.         return;
  239.     } else if (sscanf(buf, "%8lx\n", &txt_offset) < 1 || txt_offset <= 0)
  240.         goto bad_data_file;
  241.  
  242.     /* look for the appropriate entry */
  243.     while (fgets(buf,BUFSZ,fp)) {
  244.         if (*buf == '.') break;  /* we passed last entry without success */
  245.  
  246.         if (!digit(*buf)) {
  247.         if (!(ep = index(buf, '\n'))) goto bad_data_file;
  248.         *ep = 0;
  249.         if (pmatch(buf, inp) || (alt && pmatch(buf, alt))) {
  250.             found_in_file = TRUE;
  251.             break;
  252.         }
  253.         }
  254.     }
  255.     }
  256.  
  257.     if(found_in_file) {
  258.     long entry_offset;
  259.     int  entry_count;
  260.     int  i;
  261.  
  262.     /* skip over other possible matches for the info */
  263.     do {
  264.         if (!fgets(buf, BUFSZ, fp)) goto bad_data_file;
  265.     } while (!digit(*buf));
  266.     if (sscanf(buf, "%ld,%d\n", &entry_offset, &entry_count) < 2) {
  267. bad_data_file:    impossible("'data' file in wrong format");
  268.         (void) fclose(fp);
  269.         return;
  270.     }
  271.  
  272.     if (user_typed_name || yn("More info?") == 'y') {
  273.         winid datawin;
  274.  
  275.         if (fseek(fp, txt_offset + entry_offset, SEEK_SET) < 0) {
  276.         pline("? Seek error on 'data' file!");
  277.         (void) fclose(fp);
  278.         return;
  279.         }
  280.         datawin = create_nhwindow(NHW_TEXT);
  281.         for (i = 0; i < entry_count; i++) {
  282.         if (!fgets(buf, BUFSZ, fp)) goto bad_data_file;
  283.         if ((ep = index(buf, '\n')) != 0) *ep = 0;
  284.         if (index(buf+1, '\t') != 0) (void) tabexpand(buf+1);
  285.         putstr(datawin, 0, buf+1);
  286.         }
  287.         display_nhwindow(datawin, FALSE);
  288.         destroy_nhwindow(datawin);
  289.     }
  290.     } else if (user_typed_name)
  291.     pline("I don't have any information on those things.");
  292.  
  293.     (void) fclose(fp);
  294. }
  295.  
  296. static int
  297. do_look(quick)
  298.     boolean quick;    /* use cursor && don't search for "more info" */
  299. {
  300.     char    out_str[BUFSZ], look_buf[BUFSZ];
  301.     const char    *firstmatch = 0;
  302.     int     i;
  303.     int     sym;        /* typed symbol or converted glyph */
  304.     int        found;        /* count of matching syms found */
  305.     coord   cc;            /* screen pos of unknown glyph */
  306.     boolean save_verbose;    /* saved value of flags.verbose */
  307.     boolean from_screen;    /* question from the screen */
  308.     boolean need_to_look;    /* need to get explan. from glyph */
  309.     static const char *mon_interior = "the interior of a monster";
  310.  
  311.     if (quick) {
  312.     from_screen = TRUE;    /* yes, we want to use the cursor */
  313.     } else {
  314.     i = ynq("Specify unknown object by cursor?");
  315.     if (i == 'q') return 0;
  316.     from_screen = (i == 'y');
  317.     }
  318.  
  319.     if (from_screen) {
  320.     cc.x = u.ux;
  321.     cc.y = u.uy;
  322.     sym = 0;        /* gcc -Wall lint */
  323.     } else {
  324.     getlin("Specify what? (type the word)", out_str);
  325.     if (out_str[0] == '\0' || out_str[0] == '\033')
  326.         return 0;
  327.  
  328.     if (out_str[1]) {    /* user typed in a complete string */
  329.         checkfile(out_str, TRUE);
  330.         return 0;
  331.     }
  332.     sym = out_str[0];
  333.     }
  334.  
  335.     /* Save the verbose flag, we change it later. */
  336.     save_verbose = flags.verbose;
  337.     flags.verbose = flags.verbose && !quick;
  338.     /*
  339.      * The user typed one letter, or we're identifying from the screen.
  340.      */
  341.     do {
  342.     /* Reset some variables. */
  343.     need_to_look = FALSE;
  344.     found = 0;
  345.     out_str[0] = '\0';
  346.  
  347.     if (from_screen) {
  348.         int glyph;    /* glyph at selected position */
  349.  
  350.         if (flags.verbose)
  351.         pline("Please move the cursor to an unknown object.");
  352.         else
  353.         pline("Pick an object.");
  354.  
  355.         getpos(&cc, FALSE, "an unknown object");
  356.         if (cc.x < 0) {
  357.         flags.verbose = save_verbose;
  358.         return 0;    /* done */
  359.         }
  360.         flags.verbose = FALSE;    /* only print long question once */
  361.  
  362.         /* Convert the glyph at the selected position to a symbol. */
  363.         glyph = glyph_at(cc.x,cc.y);
  364.         if (glyph_is_cmap(glyph)) {
  365.         sym = showsyms[glyph_to_cmap(glyph)];
  366.         } else if (glyph_is_trap(glyph)) {
  367.         sym = showsyms[(glyph_to_trap(glyph) == WEB) ? S_web : S_trap];
  368.         } else if (glyph_is_object(glyph)) {
  369.         sym = oc_syms[(int)objects[glyph_to_obj(glyph)].oc_class];
  370.         } else if (glyph_is_monster(glyph)) {
  371.         sym = monsyms[(int)mons[glyph_to_mon(glyph)].mlet];
  372.         } else if (glyph_is_swallow(glyph)) {
  373.         sym = showsyms[glyph_to_swallow(glyph)+S_sw_tl];
  374.         } else {
  375.         impossible("do_look:  bad glyph %d at (%d,%d)",
  376.                         glyph, (int)cc.x, (int)cc.y);
  377.         sym = ' ';
  378.         }
  379.     }
  380.  
  381.     /*
  382.      * Check all the possibilities, saving all explanations in a buffer.
  383.      * When all have been checked then the string is printed.
  384.      */
  385.  
  386.     /* Check for monsters */
  387.     for (i = 0; i < MAXMCLASSES; i++) {
  388.         if (sym == (from_screen ? monsyms[i] : def_monsyms[i])) {
  389.         need_to_look = TRUE;
  390.         if (!found) {
  391.             Sprintf(out_str, "%c       %s", sym, an(monexplain[i]));
  392.             firstmatch = monexplain[i];
  393.             found++;
  394.         } else {
  395.             found += append_str(out_str, an(monexplain[i]));
  396.         }
  397.         }
  398.     }
  399.  
  400.     /*
  401.      * Special case: if identifying from the screen, and we're swallowed,
  402.      * and looking at something other than our own symbol, then just say
  403.      * "the interior of a monster".
  404.      */
  405.     if (u.uswallow && from_screen && is_swallow_sym(sym)) {
  406.         if (!found) {
  407.         Sprintf(out_str, "%c       %s", sym, mon_interior);
  408.         firstmatch = mon_interior;
  409.         } else {
  410.         found += append_str(out_str, mon_interior);
  411.         }
  412.         need_to_look = TRUE;
  413.     }
  414.  
  415.     /* Now check for objects */
  416.     for (i = 1; i < MAXOCLASSES; i++) {
  417.         if (sym == (from_screen ? oc_syms[i] : def_oc_syms[i])) {
  418.         need_to_look = TRUE;
  419.         if (!found) {
  420.             Sprintf(out_str, "%c       %s", sym, an(objexplain[i]));
  421.             firstmatch = objexplain[i];
  422.             found++;
  423.         } else {
  424.             found += append_str(out_str, an(objexplain[i]));
  425.         }
  426.         }
  427.     }
  428.  
  429.     /* Now check for graphics symbols */
  430.     for (i = 0; i < MAXPCHARS; i++) {
  431.         if (sym == (from_screen ? showsyms[i] : defsyms[i].sym) &&
  432.                         (*defsyms[i].explanation)) {
  433.         if (!found) {
  434.             Sprintf(out_str, "%c       %s",
  435.                         sym, an(defsyms[i].explanation));
  436.             firstmatch = defsyms[i].explanation;
  437.             found++;
  438.         } else if (!u.uswallow) {
  439.             found += append_str(out_str, an(defsyms[i].explanation));
  440.         }
  441.  
  442.         if (i == S_altar || i == S_trap || i == S_web)
  443.             need_to_look = TRUE;
  444.         }
  445.     }
  446.  
  447.     /*
  448.      * If we are looking at the screen, follow multiple posibilities or
  449.      * an ambigious explanation by something more detailed.
  450.      */
  451.     if (from_screen) {
  452.         if (found > 1 || need_to_look) {
  453.         lookat(cc.x, cc.y, look_buf);
  454.         firstmatch = look_buf;
  455.         if (*firstmatch) {
  456.             char temp_buf[BUFSZ];
  457.             Sprintf(temp_buf, " (%s)", firstmatch);
  458.             (void)strncat(out_str, temp_buf, BUFSZ-strlen(out_str)-1);
  459.             found = 1;    /* we have something to look up */
  460.         }
  461.         }
  462.     }
  463.  
  464.     /* Finally, print out our explanation. */
  465.     if (found) {
  466.         pline(out_str);
  467.         /* check the data file for information about this thing */
  468.         if (found == 1 && !quick && flags.help) {
  469.         char temp_buf[BUFSZ];
  470.         Strcpy(temp_buf, firstmatch);
  471.         checkfile(temp_buf, FALSE);
  472.         }
  473.     } else {
  474.         pline("I've never heard of such things.");
  475.     }
  476.  
  477.     } while (from_screen && !quick);
  478.  
  479.     flags.verbose = save_verbose;
  480.     return 0;
  481. }
  482.  
  483.  
  484. int
  485. dowhatis()
  486. {
  487.     return do_look(FALSE);
  488. }
  489.  
  490. int
  491. doquickwhatis()
  492. {
  493.     return do_look(TRUE);
  494. }
  495.  
  496. int
  497. doidtrap()
  498. {
  499.     register struct trap *trap;
  500.     register int x,y;
  501.  
  502.     if(!getdir(NULL)) return 0;
  503.     x = u.ux + u.dx;
  504.     y = u.uy + u.dy;
  505.     for(trap = ftrap; trap; trap = trap->ntrap)
  506.         if(trap->tx == x && trap->ty == y && trap->tseen) {
  507.             if(u.dz) {
  508.             if(u.dz < 0 && trap->ttyp == TRAPDOOR)
  509.                 continue;
  510.                 if(u.dz > 0 && trap->ttyp == ROCKTRAP)
  511.                 continue;
  512.             }
  513.             pline("That is a%s.",
  514.               traps[ Hallucination ? rn1(TRAPNUM-3, 3) :
  515.                 trap->ttyp]);
  516.             return 0;
  517.         }
  518.     pline("I can't see a trap there.");
  519.     return 0;
  520. }
  521.  
  522. int
  523. dowhatdoes()
  524. {
  525.     FILE *fp;
  526.     char bufr[BUFSZ+6];
  527.     register char *buf = &bufr[6], *ep, q, ctrl, meta;
  528.  
  529.     fp = fopen_datafile(CMDHELPFILE, "r");
  530.     if (!fp) {
  531.         pline("Cannot open data file!");
  532.         return 0;
  533.     }
  534.  
  535. #if defined(UNIX) || defined(VMS)
  536.     introff();
  537. #endif
  538.     q = yn_function("What command?", NULL, '\0');
  539. #if defined(UNIX) || defined(VMS)
  540.     intron();
  541. #endif
  542.     ctrl = ((q <= '\033') ? (q - 1 + 'A') : 0);
  543.     meta = ((0x80 & q) ? (0x7f & q) : 0);
  544.     while(fgets(buf,BUFSZ,fp))
  545.         if ((ctrl && *buf=='^' && *(buf+1)==ctrl) ||
  546.         (meta && *buf=='M' && *(buf+1)=='-' && *(buf+2)==meta) ||
  547.         *buf==q) {
  548.         ep = index(buf, '\n');
  549.         if(ep) *ep = 0;
  550.         if (ctrl && buf[2] == '\t'){
  551.             buf = bufr + 1;
  552.             (void) strncpy(buf, "^?      ", 8);
  553.             buf[1] = ctrl;
  554.         } else if (meta && buf[3] == '\t'){
  555.             buf = bufr + 2;
  556.             (void) strncpy(buf, "M-?     ", 8);
  557.             buf[2] = meta;
  558.         } else if(buf[1] == '\t'){
  559.             buf = bufr;
  560.             buf[0] = q;
  561.             (void) strncpy(buf+1, "       ", 7);
  562.         }
  563.         pline("%s", buf);
  564.         (void) fclose(fp);
  565.         return 0;
  566.         }
  567.     pline("I've never heard of such commands.");
  568.     (void) fclose(fp);
  569.     return 0;
  570. }
  571.  
  572. /* data for help_menu() */
  573. static const char *help_menu_items[] = {
  574.     "Information available:",
  575.     "",
  576.     "a.  Long description of the game and commands.",
  577.     "b.  List of game commands.",
  578.     "c.  Concise history of NetHack.",
  579.     "d.  Info on a character in the game display.",
  580.     "e.  Info on what a given key does.",
  581.     "f.  List of game options.",
  582.     "g.  Longer explanation of game options.",
  583.     "h.  List of extended commands.",
  584.     "i.  The NetHack license.",
  585. #ifdef PORT_HELP
  586.     "j.  %s-specific help and commands.",
  587. #endif
  588. #ifdef WIZARD
  589. # ifdef PORT_HELP
  590. # define WIZHLP_SLOT 12    /* assumed to be next to last by code below */
  591.     "k.  List of wizard-mode commands.",
  592. # else
  593. # define WIZHLP_SLOT 11    /* assumed to be next to last by code below */
  594.     "j.  List of wizard-mode commands.",
  595. # endif
  596. #endif
  597.     "",
  598.     NULL
  599. };
  600.  
  601. static char
  602. help_menu()
  603. {
  604.     winid tmpwin = create_nhwindow(NHW_MENU);
  605. #ifdef PORT_HELP
  606.     char helpbuf[QBUFSZ];
  607. #endif
  608.     char hc;
  609.     register int i;
  610.  
  611.     start_menu(tmpwin);
  612. #ifdef WIZARD
  613.     if (!wizard) help_menu_items[WIZHLP_SLOT] = "",
  614.              help_menu_items[WIZHLP_SLOT+1] = NULL;
  615. #endif
  616.     for (i = 0; help_menu_items[i]; i++)
  617. #ifdef PORT_HELP
  618.         /* port-specific line has a %s in it for the PORT_ID */
  619.         if (index(help_menu_items[i], '%')) {
  620.         Sprintf(helpbuf, help_menu_items[i], PORT_ID);
  621.         add_menu(tmpwin, helpbuf[0], 0, helpbuf);
  622.         } else
  623. #endif
  624.         {
  625.         add_menu(tmpwin, i ? *help_menu_items[i] : 0, 0,
  626.              help_menu_items[i]);
  627.         }
  628.     end_menu(tmpwin, '\033', "\033", "Select one item or ESC: ");
  629.     hc = select_menu(tmpwin);
  630.     destroy_nhwindow(tmpwin);
  631.     return hc;
  632. }
  633.  
  634. int
  635. dohelp()
  636. {
  637.     char hc = help_menu();
  638.     if (!index(quitchars, hc)) {
  639.         switch(hc) {
  640.             case 'a':  display_file(HELP, TRUE);  break;
  641.             case 'b':  display_file(SHELP, TRUE);  break;
  642.             case 'c':  (void) dohistory();  break;
  643.             case 'd':  (void) dowhatis();  break;
  644.             case 'e':  (void) dowhatdoes();  break;
  645.             case 'f':  option_help();  break;
  646.             case 'g':  display_file(OPTIONFILE, TRUE);  break;
  647.             case 'h':  (void) doextlist();  break;
  648.             case 'i':  display_file(LICENSE, TRUE);  break;
  649. #ifdef PORT_HELP
  650.             case 'j':  port_help();  break;
  651. # ifdef WIZARD
  652.             case 'k':  display_file(DEBUGHELP, TRUE);  break;
  653. # endif
  654. #else
  655. # ifdef WIZARD
  656.             case 'j':  display_file(DEBUGHELP, TRUE);  break;
  657. # endif
  658. #endif
  659.         }
  660.     }
  661.     return 0;
  662. }
  663.  
  664. int
  665. dohistory()
  666. {
  667.     display_file(HISTORY, TRUE);
  668.     return 0;
  669. }
  670.  
  671. /*pager.c*/
  672.